//
// errorhandler.cpp
// libprotea
// Tools Library for the Protea Project / Functionality for handling errors and exceptions
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

#include "defines.h"
#include "errorhandler.h"
#include "AppHandler/AppInterface.h"
#include "Fields/fields.h"
#include "OSDep/files.h"
#include "defns.h"
using namespace ProteaApp1;

#include <stdarg.h>
#include <limits.h>

XCTLS XCLanguageCode;


long XCGetLanguage() {
	return (long)XCLanguageCode.GetData();
};

void XCSetLanguage( long value ) {
	XCLanguageCode.SetData( (void*)value );
};

//
// Code for searching the error catalog
//

/// Searches the error catalog for the message number and returns the error message
char* FindErrorMessage( int number, int appnumber ) {
	XCFileTransport input( NULL );
	char filename[ MAX_PATH ];
	static buffer_msg_defn record;

	int language = (int)XCLanguageCode.GetData();
	buffer_msg_hdr index_record;

	record.number = INT_MIN;

	sprintf( filename, "%sMESSAGES%d.Appdic", errloc, appnumber );

//	cout << "Searching for error message: " << number << " in " << filename << endl;
	try {
retry:
		// Open the file
		input.Open( filename, read_only, open_existing );

		// Read the index record, this tells us how many records are available
		input.Recv( &index_record, sizeof( buffer_msg_hdr ) );

		int messages = index_record.number;
		int seek = 0;

		// Allocate and read the indexes
		buffer_msg_hdr* hdr = new buffer_msg_hdr[ messages ];
		input.Recv( hdr, sizeof( buffer_msg_hdr ) * index_record.number );

		// Now find our message number in the index
		for (int i=0; i<messages; i++) {
			if (hdr[i].number == number) {
				seek = hdr[i].index;
				break;
			};
		};

		// Attempt to find the record
		input.Seek( seek );
		try {
			while (!input.Eof() && number >= record.number) {
				input.Recv( &record, sizeof( buffer_msg_defn ) );
				if (record.number == number && record.lang_id == language) {
					input.Close();
					delete [] hdr;
					return record.message;
				};
			};
		} catch( ... ) {};

		delete [] hdr;
		input.Close();
/*
		// Retry using base language if not found...
		if (language != 0) {
			language = 0;
			input.Close();
			goto retry;
		};
*/
	} catch( ... ) {
		// Defaults needed for LoadAppdic - we have no message file
		switch( number ) {
			case ERR_DATABASE:
				strcpy( record.message, "Database error: %s" );
				break;

			case ERR_OS_ERROR:
				strcpy( record.message, "Operating system error: %s" );
				break;

			case ERR_OS_ERROR_PARM:
				strcpy( record.message, "Operating system error: %s [%s]" );
				break;

			case ERR_BAD_XSIROOT:
				strcpy( record.message, "$XSIROOT variable not set.  Please set the $XSIROOT variable to point to the base directory of your Protea Project installation." );
				break;

			case ERR_NO_LICENSE_FILE:
				strcpy( record.message, "Unable to locate license file" );
				break;

			case ERR_INV_LICENSE:
				strcpy( record.message, "Licensing failure: %m" );
				break;

			case ERR_LICENSE_IS_EXPIRED:
				strcpy( record.message, "License is expired" );
				break;

			case ERR_LICENSE_INVALID:
				strcpy( record.message, "License file is invalid" );
				break;

			case ERR_LICENSE_MACHINE:
				strcpy( record.message, "Not licensed on this machine" );
				break;

			default:
				cout << "Unable to open error file!" << endl;
				return NULL;
		};

		// Return the result
		return record.message;
	};

	return NULL;
};


XCToolsException::XCToolsException( int number, int err_field, int err_order, int line_num ) {
	errnum = number;
	line = line_num;
	usererrnum = 0;
	field = err_field;
	order = err_order;
	description = NULL;

	MakeDescr( NULL );
};

XCToolsException::XCToolsException( int number, char* descr, int err_field, int err_order, int line_num ) {
	errnum = USER_ERROR_NUMBER;
	line = line_num;
	usererrnum = number;
	field = err_field;
	order = err_order;

	description = strdup( descr );
};


XCToolsException::XCToolsException( const XCToolsException& parent ) {
	//cout << "ToolsException copy constructor" << endl;

	errnum = parent.errnum;
	line = parent.line;
	usererrnum = parent.usererrnum;
	field = parent.field;
	order = parent.order;

	if (parent.description)
		description = strdup( parent.description );
	else
		description = NULL;
};


void XCToolsException::MakeDescr( XCAppInterface* ) {
};


XCToolsException::~XCToolsException() {
//	cout << "ToolsException destructor" << endl;

	if (description) {
		free( description );
		description = NULL;
	};
};



int XCToolsException::GetErrorNumber() { 
	return errnum; 
};


int XCToolsException::GetLineNumber() { 
	return line;
};

char* XCToolsException::GetDescription() {
	return description;
};

void XCToolsException::SetOrder( int new_order ) {
	order = new_order;
};

int XCToolsException::GetOrder() {
	return order;
};

int XCToolsException::GetField() {
	return field;
};

void XCToolsException::ClearLine() {
	line = 0;
};


void XCToolsException::OutputCallStack( XCAppInterface* app ) {
	::XCOutputCallStack( app, GetErrorNumber(), GetDescription() );
};


void XCToolsException::Clear() {
	XCClearCallStack();
};





XCToolsError::~XCToolsError() {
//	cout << "ToolsException destructor" << endl;

	if (description) {
		free( description );
		description = NULL;
	};
};


XCToolsError::XCToolsError( int number, int field, int order ) : XCToolsException( number, field, order, 0 ) {
	// Reset the parm list
	parmcount = 0;
};

XCToolsError::XCToolsError( int number, int line, XCAppInterface* app, int field, int order, int count, ... ) : XCToolsException( number, field, order, line ) {
	va_list parmlist;
	void* temp;

	// Reset the parm list
	parmcount = 0;

	// Read our arguments
	va_start( parmlist, count );
	while (count) {
		temp = va_arg( parmlist, void* );
		AddParm( temp );
		count--;
	};
	va_end( parmlist );

	// Formulate the description
	MakeDescr( app );
};


void XCToolsError::AddParm( void* param ) {
	parms[ parmcount++ ] = param;
};



XCToolsError::XCToolsError( const XCToolsError& parent ): XCToolsException( parent ) {
};



XCToolsError* XCToolsError::Copy() const {
	return new XCToolsError( *this );
};




void XCToolsError::MakeDescr( XCAppInterface* app ) {
	char message[ 2048 ];
	char temp[ 2048 ];
	char replace[ 2048 ];
	char* ptemp = NULL;
	char* end;
	int counter = 0;
	int size;

	// If we already have an error defined (system error, etc)
	if (errnum == -1)
		return;

	if (!InErrorState) {
		InErrorState = true;
		if (app)
			ptemp = FindErrorMessage( errnum, app->GetAppNumber() );
		else
			ptemp = FindErrorMessage( errnum, 1 );
		InErrorState = false;
	};

	if (ptemp)
		strcpy( message, ptemp );
	else {
		if (errnum == ERR_OS_ERROR)
			strcpy( message, "OS: %s" );
		else if (errnum == ERR_OS_ERROR_PARM) 
			strcpy( message, "OS: %s [%s]" );
		else {
			if (description)
				free( description );
			sprintf( message, "Unable to find error description for message %d", errnum );
			description = (char*)malloc( strlen( message ) + 10 );
			sprintf( description, "%.5d: %s", 0, message );
			return;
		};
	};

	// Do our lookup of stuff
	char* pos = strchr( message, '%' );
	while (pos) {
		end = pos + 1;
		if (*end != '%') {
			while ( (*end < 'a' && *end > 'z') && (*end < 'A' && *end > 'Z'))
				end++;

			void* current_parm = parms[ counter++ ];

			size = (int)(end - pos) + 1;

			switch( *end ) {
				case ' ': // Most likely an error...
					strcpy( replace, "[BAD MESSAGE REPLACEMENT OPERATOR]" );
					break;

				case 'm': { // Concat a message
					long number = (long)current_parm;
					char* value = FindErrorMessage( number, app ? app->GetAppNumber() : 1 );
					if (value) 
						strcpy( replace, value );
					else
						strcpy( replace, "[MISSING DESCRIPTION MESSAGE]" );
					break;
				};

				case 'F': {	// We are dealing with a field
					XCField* source = (XCField*)current_parm;

					switch( *(end+1) ) {
						case 'n':
							strcpy( replace, source->GetFieldName() );
							break;

						case 'd': {	// Message replace
							long number = source->GetDescrMessage();
							char* value = FindErrorMessage( number, app->GetAppNumber() );
							if (value) 
								strcpy( replace, value );
							else
								strcpy( replace, source->GetFieldName() );
							};
							break;

						case 'l':
							sprintf( replace, "%ld", source->GetLength() );
							break;

						case 't':
							sprintf( replace, "%d", source->GetType() );
							break;

						case '#':
							sprintf( replace, "%ld", source->GetFieldNumber() );
							break;

						case 'v':
							source->charoutput( replace, 1000 );
							break;

						default:
							sprintf( replace, "Unknown" );
					};

					// Two characters long
					size++;
					break;
				};

				default: {
					char sprinttemp[ 20 ];
					memcpy( sprinttemp, pos, size );
					sprinttemp[ size ] = 0;
					sprintf( replace, sprinttemp, current_parm );
				};
			};
		} else {
			size = 0;
			strcpy( replace, "%" );
		};

		*pos = 0;
		sprintf( temp, "%s%s%s", message, replace, pos+size );
		strcpy( message, temp );

		pos = strchr( pos+strlen(replace), '%' );
	};

	if (description)
		free(description);

	description = (char*)malloc( strlen( message ) + 10 );
	sprintf( description, "%.5d: %s", errnum, message );
};




